﻿// DExSerClientDemo_CPP.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <thread>
#include <ctime>

#include "DExSerClientV1.h"

//链接静态库
#pragma comment (lib,"ws2_32.lib")

using namespace std;

//#define __UDPSerListenDemo

int16_t UDPSendTo(short sendHostShort, const char* sendIP, char* sendDatas, uint16_t iDataLen);
int16_t UDPSendTo(uint16_t sendHostShort, uint8_t* sendIP, uint8_t* sendDatas, uint16_t iDataLen);
int16_t UDPSendTo(sockaddr_in* DestAddress, uint8_t* sendDatas, uint16_t iDataLen);
void UDPListen(sockaddr_in listenAddress); 
void UDPThdReceFrom(SOCKET udpSock);
void ThdGetLocalTime();

#define _TxBufSize (uint16_t)256
#define _RegCount  (uint8_t)16
#define _RegStarAddress (uint8_t)10
#define _PLCType_WFDRVX (uint8_t)5

struct tm localTime;
uint16_t RegValues[_RegCount] = { 0 };
uint8_t TxBuf[_TxBufSize] = { 0 };
uint16_t PLCSN[] = { 0x0102,0x0304,0x0506,0x0708,0x090A,0x0B0C,0x0D0E,0x0F10};

int main()
{
    char const* serverIP = "192.168.13.224";
    short serverPort = 19523;

    PDExClient tDExClient = DExClient_init(PLCSN, NULL);

    WSADATA  wsData;
    int nret = WSAStartup(MAKEWORD(2, 2), &wsData);
    if (nret != 0)
    {
        return 0;
    }
    printf(">>>SendIPEPoint  %s:%d\n", serverIP, serverPort);

#ifdef __UDPSerListenDemo
    char const* listenIP = "192.168.0.47";
    uint16_t listenPort = 51555;
    printf(">>>ReceiveIPEPoint  %s:%d\n", listenIP, listenPort);
    sockaddr_in listenAddress;
    int len = sizeof(listenAddress);
    inet_pton(AF_INET, listenIP, &listenAddress.sin_addr);
    listenAddress.sin_family = AF_INET;
    listenAddress.sin_port = htons(listenPort);

    thread t1(UDPListen, listenAddress);

    // 判断线程是否可以调用join函数
    //bool isJoin1 = t1.joinable();

    // 分离线程，让线程在主线程关闭后依然能够运行
    //t1.detach();
#endif

    thread t2(ThdGetLocalTime);
    bool isJoin2 = t2.joinable();

    for (int i = 0; i < _RegCount; i++)
        RegValues[i] = 0xFF00 | i;

    // 创建UDP协议Socket 
    SOCKET udpSockClient = socket(AF_INET, SOCK_DGRAM, 0);
    thread t3(UDPThdReceFrom, udpSockClient);

    sockaddr_in serAddress;
    serAddress.sin_family = AF_INET;
    serAddress.sin_port = htons(serverPort);
    inet_pton(AF_INET, serverIP, &serAddress.sin_addr);
    int iLastPort = 0;

    while (1)
    {
        memset(TxBuf, 0, sizeof(TxBuf));
        cout << ">>>pelase input dest port:";
        char sendBuf[128];
        cin.getline(sendBuf, 64);
        int iPort = atoi(sendBuf);
        if (iPort <= 0)
            iPort=iLastPort ;
        if ((iPort > 1000) && (iPort < 0xFFFF))
            iLastPort = iPort;
        else
            iPort = iLastPort;
        if ((iLastPort > 1000) && (iLastPort < 0xFFFF))
        {

            uint16_t iResTranID = 0;
            uint16_t iDLen = DExClient_Upload(TxBuf, _TxBufSize, iResTranID,
                _RegStarAddress, _RegCount, RegValues, _PLCType_WFDRVX);

            serAddress.sin_port = htons(iLastPort);
            printf(">senddatas to %s:%d\n", serverIP, iLastPort);
            uint16_t iResult = sendto(udpSockClient, (char*)TxBuf, iDLen, 0, (sockaddr*)&serAddress, sizeof(sockaddr_in));
            if (iResult > 0) {
                // 显示发送的数据
                printf("[Client]>SendDatas[%s:%d](%d):", serverIP, iLastPort, iDLen);
                for (int i = 0; i < iDLen; i++)
                    printf("0x%02X ", (uint8_t)TxBuf[i]);
                printf("\n");
            }
            Sleep(200);
        }
        else
            printf(">Port Error\n");
    }
    std::cout << "Hello World!\n";
    closesocket(udpSockClient);
    WSACleanup();
}

void ThdGetLocalTime()
{
    while (1)
    {
        time_t now = time(nullptr);
        // 将时间戳转换为本地时间
        localtime_s(&localTime, &now);
        localTime.tm_year += 1900;
        localTime.tm_mon += 1;
        RegValues[0] = localTime.tm_year;
        RegValues[1] = localTime.tm_mon;
        RegValues[2] = localTime.tm_mday;
        RegValues[3] = localTime.tm_wday;
        RegValues[4] = localTime.tm_hour;
        RegValues[5] = localTime.tm_min;
        RegValues[6] = localTime.tm_sec;
    }
}

/// <summary>
/// 启动UDP监听，线程中调用
/// </summary>
/// <param name="receiveIP">本地监听IP地址</param>
/// <param name="receiveHostShort">本地监听端口</param>
void UDPListen(sockaddr_in listenAddress)
{
    SOCKET udpSockSer = NULL;
    //接收UDP编写
    int len = sizeof(listenAddress);
    udpSockSer = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSockSer != INVALID_SOCKET)
    {
        bind(udpSockSer, (sockaddr*)&listenAddress, len);

        // 设置recvfrom超时，避免阻塞
        struct timeval tv_out;
        tv_out.tv_sec = 1;
        tv_out.tv_usec = 0;
        if (setsockopt(udpSockSer, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_out, sizeof(tv_out)) != 0)
        {
            return;
        }

        sockaddr_in  recSa;
        int len = sizeof(recSa);
        while (true)
        {
            char buf[1024];
            memset(buf, 0, 1024);
            int nlen = recvfrom(udpSockSer, buf, 1024, 0, (sockaddr*)&recSa, &len);
            if (nlen > 0)
            {
                char recfromIP[64] = { 0 };
                memset(recfromIP, 0, sizeof(recfromIP));
                inet_ntop(AF_INET, &(recSa.sin_addr), recfromIP, INET_ADDRSTRLEN);
                printf("\n[Ser]>ReceDatas[%s:%d](%d):", recfromIP, recSa.sin_port, nlen);
                for (int i = 0; i < nlen; i++)
                    printf("0x%02X ", (uint8_t)buf[i]);
                memset(buf, 0, 1024);
                printf("\n>>>>>>>>>>>>>>>>>\n");


            }
        }
        closesocket(udpSockSer);
    }
}

/// <summary>
/// 线程函数。用于UDP Socket的接受数据
/// </summary>
/// <param name="udpSock"></param>
void UDPThdReceFrom(SOCKET udpSock)
{
    if (udpSock != INVALID_SOCKET)
    {
        // 设置recvfrom超时，避免阻塞
        struct timeval tv_out;
        tv_out.tv_sec = 1;
        tv_out.tv_usec = 0;
        if (setsockopt(udpSock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_out, sizeof(tv_out)) != 0)
        {
            return;
        }

        sockaddr_in  recSa;
        int len = sizeof(recSa);
        char buf[1024];
        memset(buf, 0, 1024);
        while (true)
        {
            int nlen = recvfrom(udpSock, buf, 1024, 0, (sockaddr*)&recSa, &len);
            if (nlen > 0)
            {
                uint16_t iRecD= DExClient_RecDatas((uint8_t *)buf, nlen);
                char recfromIP[128] = { 0 };
                memset(recfromIP, 0, sizeof(recfromIP));
                // 将IP地址转换为字符串显示
                inet_ntop(AF_INET, &(recSa.sin_addr), recfromIP, INET_ADDRSTRLEN);

                // 显示收到的数据
                printf("[Client]>ReceDatas[%s:%d](%d):", recfromIP, recSa.sin_port, nlen);
                for (int i = 0; i < nlen; i++)
                    printf("0x%02X ", (uint8_t)buf[i]);
                memset(buf, 0, 1024);
                printf("\n");
                memset(buf, 0, 1024);
            }
            Sleep(50);
        }
    }
}

/// <summary>
/// UDP发送数据
/// </summary>
/// <param name="sendHostShort">目标端口</param>
/// <param name="sendIP"></param>
/// <param name="sendDatas"></param>
/// <param name="iDataLen"></param>
/// <returns></returns>
int16_t UDPSendTo(short sendHostShort, const char* sendIP,char * sendDatas,uint16_t iDataLen )
{
    sockaddr_in sClient;
    sClient.sin_family = AF_INET;
    sClient.sin_port = htons(sendHostShort);    

    return UDPSendTo(&sClient, (uint8_t*)sendDatas, iDataLen);
}

/// <summary>
/// UDP发送数据
/// </summary>
/// <param name="sendHostShort"></param>
/// <param name="sendIP"></param>
/// <param name="sendDatas"></param>
/// <param name="iDataLen"></param>
/// <returns></returns>
int16_t UDPSendTo(uint16_t sendHostShort, uint8_t * sendIP, uint8_t* sendDatas, uint16_t iDataLen)
{
    sockaddr_in sClient;
    sClient.sin_family = AF_INET;
    sClient.sin_port = htons(sendHostShort);

    sClient.sin_addr.S_un.S_un_b.s_b1 = *(sendIP + 0);
    sClient.sin_addr.S_un.S_un_b.s_b2 = *(sendIP + 1);
    sClient.sin_addr.S_un.S_un_b.s_b3 = *(sendIP + 2);
    sClient.sin_addr.S_un.S_un_b.s_b4 = *(sendIP + 3);

    return UDPSendTo(&sClient, sendDatas, iDataLen);
}

int16_t UDPSendTo(sockaddr_in * DestAddress ,uint8_t* sendDatas, uint16_t iDataLen)
{
    if (DestAddress == NULL)
        return -2;

    SOCKET psock = socket(AF_INET, SOCK_DGRAM, 0);

    uint16_t iResult = sendto(psock, (char*)sendDatas, iDataLen, 0, (sockaddr*)DestAddress, sizeof(sockaddr_in));

    closesocket(psock);
    return iResult;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
